package cluster

import(
	"github.com/sniperHW/kendynet"
	"github.com/golang/protobuf/proto"
	"fmt"
	"math/rand"
	"sync"
	"github.com/sniperHW/kendynet/rpc"
	//"net"
	"sort"
)

var (
	mtx              sync.Mutex	
	idEndPointMap    map[PeerID]*endPoint
	ttEndPointMap 	 map[string]*typeEndPointMap
	sessionPeerIDMap map[kendynet.StreamSession] PeerID
)

//类型@ip:port唯一标识一个端点
type connection struct {
	session    		 kendynet.StreamSession
	rpcCli    		*rpc.RPCClient
	nextHeartbeat   *int64
}

type endPoint struct {
	tt         	  string
	ip         	  string
	port       	  int32	
	pendingMsg    []proto.Message      //待发送的消息
	pendingCall   []*rpcCall           //待发起的rpc请求
	dialing       bool
	conn      	  *connection
	str           string
	peer          PeerID
}

type typeEndPointMap struct {
	tt           string
	endPoints    []*endPoint
}

func (this *typeEndPointMap) sort() {
	sort.Slice(this.endPoints,func (i,j int) bool {
		return this.endPoints[i].toString() < this.endPoints[j].toString()
	})
}

func (this *typeEndPointMap) removeEndPoint(peer PeerID) {
	for i,v := range(this.endPoints) {
		if peer.ToString() == v.toString() {
			this.endPoints[i] = this.endPoints[len(this.endPoints) - 1]
			this.endPoints = this.endPoints[:len(this.endPoints) - 1]
			this.sort()
			break
		}
	}
}

func (this *typeEndPointMap) addEndPoint(end *endPoint) {

	find := false
	for _,v := range(this.endPoints) {
		if end.toString() == v.toString() {
			find = true
			break
		}
	}

	if !find {
		this.endPoints = append(this.endPoints,end)
		this.sort()
	}
}

func (this *typeEndPointMap) random() (PeerID,error) {
	size := len(this.endPoints)
	if size > 0 {
		i := rand.Int()%size
		return this.endPoints[i].toPeerID(),nil
	} else {
		return MakePeerID("","",0),fmt.Errorf("no available peer")
	}
}


func addEndPoint(peer PeerID) {
	defer mtx.Unlock()
	mtx.Lock()
	service,err := PeerID2Service(peer)
	if nil != err {
		return
	}

	end := idEndPointMap[peer]
	if nil != end {
		return
	}

	end = &endPoint {
		tt 		: service.tt,
		ip 		: service.ip,
		port    : service.port,
		str     : fmt.Sprintf("%s@%s:%d",service.tt,service.ip,service.port),
		peer    : PeerID(fmt.Sprintf("%s@%s:%d",service.tt,service.ip,service.port)),			
	}

	ttMap := ttEndPointMap[service.tt]
	if nil == ttMap {
		ttMap = &typeEndPointMap {
			tt : service.tt,
			endPoints : []*endPoint{},
		}
		ttEndPointMap[service.tt] = ttMap
	}

	idEndPointMap[peer] = end
	ttMap.addEndPoint(end)
	Infoln("addEndPoint",peer)
}


func removeEndPoint(peer PeerID) {
	defer mtx.Unlock()
	mtx.Lock()	
	if end,ok := idEndPointMap[peer]; ok {
		Infoln("remove endPoint",peer)
		ttMap := ttEndPointMap[end.tt]
		
		if nil != ttMap {
			ttMap.removeEndPoint(peer)
		}

		if nil != end.conn {
			end.conn.session.Close("remove endPoint",0)
		}
	}
}


func (this *endPoint) toPeerID() PeerID {
	return this.peer
}

func (this *endPoint) toString() string {
	return this.str
}

func getEndPointByID(id PeerID) *endPoint {
	defer mtx.Unlock()
	mtx.Lock()	
	if end,ok := idEndPointMap[id]; ok {
		return end
	}
	return nil
}

func addSessionPeerID(session kendynet.StreamSession,peerID PeerID) {
	defer mtx.Unlock()
	mtx.Lock()	
	sessionPeerIDMap[session] = peerID
}

func remSessionPeerID(session kendynet.StreamSession) {
	defer mtx.Unlock()
	mtx.Lock()	
	delete(sessionPeerIDMap,session)	
}

func GetPeerIDBySession(session kendynet.StreamSession) (PeerID,error) {
	defer mtx.Unlock()
	mtx.Lock()

	if p,ok := sessionPeerIDMap[session]; ok {
		return p,nil
	}
	return MakePeerID("","",0),fmt.Errorf("unknow session")
}

//随机获取一个类型为tt的节点id
func Random(tt string) (PeerID,error) {
	defer mtx.Unlock()
	mtx.Lock()		
	if ttmap,ok := ttEndPointMap[tt]; ok {
		return ttmap.random()
	}
	return MakePeerID("","",0),fmt.Errorf("invaild tt")
}